use reqwest::header::{HeaderMap, HeaderValue};
use serde_derive::{Deserialize, Serialize};
use serde_json::{Value, json};

use crate::mylib::{util_date, util_file, util_string};

static CHARS: &'static [u8] = b"0123456789abcdef";

use super::my_err::MyErr;

// e签宝 接口
#[derive(Deserialize)]
pub struct Esign {
    pub domain: String,
    pub appid: String,
    pub secret: String,
}

// 获取文件上传地址得到的结果
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[allow(dead_code)]
pub struct GetUploadUrlRsBean {
    pub content_md5: String,     // 文件MD5
    pub content_type: String,    // 文件类型,固定 application/octet-stream 或 application/pdf
    pub file_id: String,         // 文件ID
    pub file_upload_url: String, // 上传地址
}

// 获取创建模板文件地址得到的结果
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[allow(dead_code)]
pub struct GetCreateTemplateUrlRsBean {
    pub doc_template_id: String,              // 合同模板ID
    pub doc_template_create_url: String,      // 制作合同模板的页面短链接（有效期24小时）
    pub doc_template_create_long_url: String, // 制作合同模板的页面长链接（有效期24小时）
}

// 填写模板生成文件的结果
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[allow(dead_code)]
pub struct GetCreateDocRsBean {
    pub file_id: String,           // 填充后生成的文件ID
    pub file_download_url: String, // 文件下载地址
}

impl Esign {
    /// 获取文件上传地址
    /// file_path : 文件相对路径
    /// file_name : 文件名称，如 ： 汽车票代售合作协议.pdf
    #[allow(dead_code)]
    pub async fn get_file_upload_url(&self, file_path: &str, file_name: &str) -> Result<GetUploadUrlRsBean, MyErr> {
        let uri_query = "/v3/files/file-upload-url"; // 

        let ext_name = util_file::get_ext_name(file_path);
        let file_size = util_file::get_file_size(file_path)?;
        let content_type = if ext_name.to_ascii_lowercase() == "pdf" {
            "application/pdf"
        } else {
            "application/octet-stream"
        };
        let md5_digest = util_file::get_file_md5_digest(file_path)?;
        let content_md5 = util_string::base64_encode_vec(&md5_digest);

        let body_json = json!({
            "contentMd5" : content_md5,
            "contentType" : content_type,
            "fileName" : file_name,
            "fileSize" : file_size,
        });

        let result = self.request_post(uri_query, body_json, "application/json").await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data").and_then(Value::as_object) {
                let file_id = data.get("fileId").and_then(Value::as_str).unwrap_or_default();
                let file_upload_url = data.get("fileUploadUrl").and_then(Value::as_str).unwrap_or_default();
                let bean = GetUploadUrlRsBean {
                    content_md5: content_md5,
                    content_type: content_type.to_string(),
                    file_id: file_id.to_string(),
                    file_upload_url: file_upload_url.to_string(),
                };
                Ok(bean)
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    /// 获取文件上传地址
    /// file_path： 文件相对路径
    /// content_md5 ： 文件MD5，上一步（get_file_upload_url）中得到
    /// content_type ： 文件类型。固定 application/octet-stream 或 application/pdf 。上一步（get_file_upload_url）中得到
    /// _file_id : 文件ID，上一步（get_file_upload_url）中得到
    /// file_upload_url ： 文件上传地址，上一步（get_file_upload_url）中得到
    #[allow(dead_code)]
    pub async fn upload_file(
        &self,
        file_path: &str,
        content_md5: &str,
        content_type: &str,
        _file_id: &str,
        file_upload_url: &str,
    ) -> Result<bool, MyErr> {
        let content_md5 = HeaderValue::from_str(content_md5).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let content_type = HeaderValue::from_str(content_type).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;

        let mut headers = HeaderMap::new();
        headers.insert("Content-MD5", content_md5);
        headers.insert("Content-Type", content_type);

        let body_binary: Vec<u8> = util_file::read_file_binary(file_path)?;

        let client = reqwest::Client::new();
        let rs = client.request(reqwest::Method::PUT, file_upload_url).headers(headers.clone()).body(body_binary).send().await;
        match rs {
            Ok(res) => match res.json::<serde_json::Value>().await {
                Ok(v) => {
                    let code = v.get("errCode").and_then(Value::as_i64).unwrap_or(-1);
                    let message = v.get("msg").and_then(Value::as_str).unwrap_or_default();
                    if code == 0 {
                        Ok(true)
                    } else {
                        Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
                    }
                }
                Err(e) => {
                    log::error!("读取URL数据失败！错误：{:?}  ", e);
                    Err(MyErr::Msg("网络异常！".to_string()))
                }
            },
            Err(e) => {
                log::error!("连接url地址失败！错误：{:?}   ", e);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        }
    }

    /// 获取创建模板的URL地址
    /// file_id : 底稿文件ID，此ID通过 获取文件上传地址（get_file_upload_url） 获取
    /// doc_template_name : 模板名称（可自定义模板名称）
    #[allow(dead_code)]
    pub async fn get_create_template_url(&self, file_id: &str, doc_template_name: &str) -> Result<GetCreateTemplateUrlRsBean, MyErr> {
        let uri_query = "/v3/doc-templates/doc-template-create-url"; //  

        let body_json = json!({
            "docTemplateName" : doc_template_name,
            "fileId" : file_id,
        });

        let result = self.request_post(uri_query, body_json, "application/json").await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data").and_then(Value::as_object) {
                let doc_template_id = data.get("docTemplateId").and_then(Value::as_str).unwrap_or_default();
                let doc_template_create_url = data.get("docTemplateCreateUrl").and_then(Value::as_str).unwrap_or_default();
                let doc_template_create_long_url = data.get("docTemplateCreateLongUrl").and_then(Value::as_str).unwrap_or_default();
                let bean = GetCreateTemplateUrlRsBean {
                    doc_template_id: doc_template_id.to_string(),
                    doc_template_create_url: doc_template_create_url.to_string(),
                    doc_template_create_long_url: doc_template_create_long_url.to_string(),
                };
                Ok(bean)
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    /// 获取编辑合同模板页面URL地址
    /// doc_template_id : 文件模板ID（通过【获取制作文件模板页面链接】(get_create_template_url)接口获取）
    #[allow(dead_code)]
    pub async fn get_edit_template_url(&self, doc_template_id: &str) -> Result<(String, String), MyErr> {
        let uri_query = "/v3/doc-templates/{docTemplateId}/doc-template-edit-url"; //  

        let body_json = json!({
            "docTemplateId" : doc_template_id, 
        });

        let result = self.request_post(uri_query, body_json, "application/json").await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data").and_then(Value::as_object) { 
                let doc_template_edit_url = data.get("docTemplateEditUrl").and_then(Value::as_str).unwrap_or_default();
                let doc_template_edit_long_url = data.get("docTemplateEditLongUrl").and_then(Value::as_str).unwrap_or_default();
                Ok((doc_template_edit_url.to_string(), doc_template_edit_long_url.to_string()))
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    /// 查询合同模板中控件详情
    /// doc_template_id : 合同模板ID（通过【获取制作文件模板页面链接】(get_create_template_url) 接口获取）
    #[allow(dead_code)]
    pub async fn get_template_element(&self, doc_template_id: &str) -> Result<Value, MyErr> {
        let uri_query = format!("/v3/doc-templates/{}", doc_template_id); //   

        let result = self.request_get(&uri_query).await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data") {
                Ok(data.to_owned())
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    /// 填写模板生成文件
    /// doc_template_id : 待填充的模板ID（通过【获取制作合同模板页面】(get_create_template_url)接口获取）
    /// file_name : 填充后生成的文件名称
    /// components_data: 控件列表
    /// required_check: 是否校验PDF模板中必填控件，默认：false
    #[allow(dead_code)]
    pub async fn create_doc_by_template(
        &self,
        doc_template_id: &str,
        file_name: &str,
        components_data: Value,
        required_check: bool,
    ) -> Result<GetCreateDocRsBean, MyErr> {
        let uri_query = "/v3/files/create-by-doc-template"; //  

        let body_json = json!({
            "docTemplateId" : doc_template_id,
            "fileName" : file_name,
            "components" : components_data,
            "requiredCheck" : required_check,
        });

        let result = self.request_post(uri_query, body_json, "application/json").await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data").and_then(Value::as_object) {
                let file_id = data.get("fileId").and_then(Value::as_str).unwrap_or_default();
                let file_download_url = data.get("fileDownloadUrl").and_then(Value::as_str).unwrap_or_default();
                let bean = GetCreateDocRsBean {
                    file_id: file_id.to_string(),
                    file_download_url: file_download_url.to_string(), 
                };
                Ok(bean)
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    /// 发起合同签署 
    #[allow(dead_code)]
    pub async fn push_sign_contract(
        &self,
        docs: Value,
        sign_flow_config: Value,
        signers: Value, 
    ) -> Result<String, MyErr> {
        let uri_query = "/v3/sign-flow/create-by-file"; //  

        let body_json = json!({
            "docs" : docs,
            "signFlowConfig" : sign_flow_config,
            "signers" : signers, 
        });

        let result = self.request_post(uri_query, body_json, "application/json").await?;

        let code = result.get("code").and_then(Value::as_i64).unwrap_or(-1);
        let message = result.get("message").and_then(Value::as_str).unwrap_or_default();
        if code == 0 {
            if let Some(data) = result.get("data").and_then(Value::as_object) {
                let sign_flow_id = data.get("signFlowId").and_then(Value::as_str).unwrap_or_default(); 
                Ok(sign_flow_id.to_string())
            } else {
                Err(MyErr::Msg(format!("获取上传地址失败，缺少data数据：{:?}", result)))
            }
        } else {
            Err(MyErr::Msg(format!("获取上传地址失败，错误码：{}，错误:{}", code, message)))
        }
    }

    

    /// 处理数据并发送请求返回结果
    async fn request_post(&self, uri_query: &str, body_json: Value, content_type: &str) -> Result<Value, MyErr> {
        let body_str = body_json.to_string();
        println!("body_str:{}", body_str);

        let body_md5_buf = util_string::md5_to_vec(&body_str);
        let body_md5 = util_string::base64_encode_vec(&body_md5_buf);
        println!("body_md5:{}", body_md5);

        let signature_str = signature_str(reqwest::Method::POST.as_str(), &body_md5, content_type, "", "", uri_query).await?;
        println!("signature_str:\n{}", signature_str);

        let signature = util_string::hmac_sha256_base64(&self.secret, &signature_str);
        println!("signature:{}", signature);

        let header_appid = HeaderValue::from_str(&self.appid).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_timestamp =
            HeaderValue::from_str(&util_date::now_milliseconds().to_string()).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_content_type = HeaderValue::from_str(content_type).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_signature = HeaderValue::from_str(&signature).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_body_md5 = HeaderValue::from_str(&body_md5).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;

        let mut headers = HeaderMap::new();
        headers.insert("X-Tsign-Open-App-Id", header_appid);
        headers.insert("X-Tsign-Open-Auth-Mode", HeaderValue::from_static("Signature"));
        headers.insert("X-Tsign-Open-Ca-Timestamp", header_timestamp);
        headers.insert("Accept", HeaderValue::from_static("*/*"));
        headers.insert("Content-Type", header_content_type);
        headers.insert("X-Tsign-Open-Ca-Signature", header_signature);
        headers.insert("Content-MD5", header_body_md5);

        //println!("\n\n\n content_type ===> \n{} \n\n", content_type);

        let url = format!("{}{}", &self.domain, uri_query);

        let client = reqwest::Client::new();
        let rs = client.request(reqwest::Method::POST, url).headers(headers.clone()).body(body_str).send().await;
        match rs {
            Ok(res) => match res.json::<serde_json::Value>().await {
                Ok(v) => Ok(v),
                Err(e) => {
                    log::error!("读取URL数据失败！错误：{:?}  ", e);
                    Err(MyErr::Msg("网络异常！".to_string()))
                }
            },
            Err(e) => {
                log::error!("连接url地址失败！错误：{:?}   ", e);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        }
    }

    /// 处理数据并发送请求返回结果
    async fn request_get(&self, uri_query: &str) -> Result<Value, MyErr> {
        let signature_str = signature_str(reqwest::Method::GET.as_str(), "", "", "", "", uri_query).await?;
        println!("signature_str:\n{}", signature_str);

        let signature = util_string::hmac_sha256_base64(&self.secret, &signature_str);
        println!("signature:{}", signature);

        let header_appid = HeaderValue::from_str(&self.appid).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_timestamp =
            HeaderValue::from_str(&util_date::now_milliseconds().to_string()).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;
        let header_signature = HeaderValue::from_str(&signature).map_err(|_| MyErr::Msg("参数错误！".to_string()))?;

        let mut headers = HeaderMap::new();
        headers.insert("X-Tsign-Open-App-Id", header_appid);
        headers.insert("X-Tsign-Open-Auth-Mode", HeaderValue::from_static("Signature"));
        headers.insert("X-Tsign-Open-Ca-Timestamp", header_timestamp);
        headers.insert("Accept", HeaderValue::from_static("*/*"));
        headers.insert("X-Tsign-Open-Ca-Signature", header_signature);

        //println!("\n\n\n content_type ===> \n{} \n\n", content_type);

        let url = format!("{}{}", &self.domain, uri_query);

        let client = reqwest::Client::new();
        let rs = client.request(reqwest::Method::GET, url).headers(headers.clone()).send().await;
        match rs {
            Ok(res) => match res.json::<serde_json::Value>().await {
                Ok(v) => Ok(v),
                Err(e) => {
                    log::error!("读取URL数据失败！错误：{:?}  ", e);
                    Err(MyErr::Msg("网络异常！".to_string()))
                }
            },
            Err(e) => {
                log::error!("连接url地址失败！错误：{:?}   ", e);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        }
    }
}

async fn signature_str(
    method: &str,
    body_md5: &str,
    content_type: &str,
    date: &str,
    headers: &str,
    uri_query: &str,
) -> Result<String, MyErr> {
    let method = method.to_uppercase(); // 请求方法要求转为大写: 如 post 转为 POST
    let headers = if util_string::is_empty(headers) {
        "".to_string()
    } else {
        format!("{}\n", headers)
    };
    let str = format!(
        "{}\n*/*\n{}\n{}\n{}\n{}{}",
        method, body_md5, content_type, date, headers, uri_query
    );
    Ok(str)
}
